import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;


public class Parser {

	
	public static ArrayList<AbstractElement> Parse (String fileName)
	{

		ArrayList<AbstractElement> elements = new ArrayList<AbstractElement>();
		try {
			BufferedReader in = new BufferedReader(new FileReader(fileName));

			String str = ""; 
			String line;
			
			//reads each line
			while ((line = in.readLine()) != null) 
			{ 

				//replaces white spaces
				line = line.replaceAll("\\t","");
				line = line.replaceAll(" ", "");
				line = line.replaceAll("\\n"," ");

				//if there is an empty line
				if (line.equals(""))
					continue;

				//appends lines until it finds the ";" character
				if (line.indexOf(";") == -1) {
					line = line.replaceAll(";", "");
					str += line;
					continue;
				}

				else {
					AbstractElement root = new FunctionCall();
					
					str += line; 

					//processes function call
					root = processFunctionCall(str, root);

					if (root == null) {
						System.err.println("Parser: Error while trying to parse " + str);
						str = "";
						continue;
					}
					
					elements.add(root);


					str = "";
				}
			} 

			in.close();


		} 

		catch (FileNotFoundException e) {
			System.err.println("Error in parser: File not found");
			return null;
		} catch (IOException e) {
			System.err.println("IOException in parser");
			return null;
		}
		catch (Exception e) {
			System.err.println("Unknown error in parser");
			return null;
		}


		return elements;

	}



	public static void printTree(AbstractElement p, String tab) {
		System.out.println(tab + p.type + " - " + p.value);
		for (int i = 0; i < p.filhos.size(); i++) {
			AbstractElement par = p.filhos.get(i);

			printTree(par, tab + "\t");
		}
	}

	public static AbstractElement processFunctionCall(String stringToParse, AbstractElement parser)
	{

		try {

			//if there is no functionCall, returns null
			if (stringToParse.indexOf("(") == -1 || stringToParse.indexOf(")") == -1) {
				System.err.println("Parser: Error while processing a function call");
				return null;
			}

			String functionName = stringToParse.substring(0, stringToParse.indexOf("("));



			parser.value = functionName;

			//if it is an if clause, changes AbstractElement object
			if (functionName.equals("if")) {
				AbstractElement p = new IfClause();
				parser = p;
			}

			//build a string with the parameters of the functionCall
			String parameters = stringToParse.substring(stringToParse.indexOf("(")+1, stringToParse.lastIndexOf(")"));

			//if functionCall has no parameters, it's done, and returns the object
			if (parameters.length() == 0)
				return parser;

			//to control parameters
			boolean inside = false;
			boolean afterCallFunction = false;

			
			if (parameters.indexOf(",") == -1 && parameters.indexOf("(") == -1) {
				String element = parameters;

				AbstractElement p = new Number();
				p.value = element;
				parser.filhos.add(p);

				return parser;
			}

			//iterates over parameters characters
			for (int i = 0; i < parameters.length(); i++) {

				String parametersget = parameters.substring(i, i+1);
				
				//if it finds a "(", it is another functionCall
				if (parametersget.equals("(")) {

					int functionCounter = 0;
					
					//counts ")" and "(" so that it can know where the parameters of a functionCall end (because it can have functionCalls inside functionCalls)
					for (int j = i; j < parameters.length(); j++) {
						String parametersgetaux = parameters.substring(j, j+1);

						if (parametersgetaux.equals(")")) {

							if (--functionCounter == 0) {
								functionCounter = j;
							}
						}

						if (parametersgetaux.equals("("))
							functionCounter++;

					}

					String functionCall = "";

					if (parameters.startsWith(",")) {
						functionCall = parameters.substring(1, parameters.indexOf(")", functionCounter)+1);
					}
					else
						functionCall = parameters.substring(0, parameters.indexOf(")", functionCounter)+1);

					
					AbstractElement p = new FunctionCall();
		
					//calls processFunctionCall recursively
					p = processFunctionCall(functionCall, p);

					//if there was an error
					if (p == null)
						return null;

					parser.filhos.add(p);

					parameters = parameters.substring(parameters.indexOf(")", functionCounter)+1);
					i = 0;
					inside = false;
					afterCallFunction = true;
	

				}

				//if it finds a "," character, there is another parameter
				else if (parametersget.equals(",") || parameters.startsWith(",")) {

					//are whe inside a functionCall parameters list?
					if (inside == false) {
						if (!afterCallFunction) {
							parameters = parameters.substring(0);
						}

						else {
							parameters = parameters.substring(1);
						}

						i = 0;
						//aux = "";
						inside = true;

					}

					else if (inside == true) {
						String element = parameters.substring(0, i);
						
						AbstractElement p = null;
						try {

							Integer.parseInt(element);

							p = new Number();

						}

						catch (NumberFormatException e) {
							//System.
							p = new Variable();
						}

						p.value = element;
						parser.filhos.add(p);

						parameters = parameters.substring(i+1, parameters.length());
						
						i = 0;
					

					}

					//if there is no more special characters
					if (parameters.indexOf(",") == -1 && parameters.indexOf("(") == -1 && parameters.indexOf(")") == -1) {
						String element = parameters;
						
						AbstractElement p = null;
						try {

							Integer.parseInt(element);

							p = new Number();

						}

						catch (NumberFormatException e) {
							//System.
							p = new Variable();
						}
						p.value = element;
						parser.filhos.add(p);

					}

					afterCallFunction = false;

				
				}

			}
		}
		catch (Exception e) {
			System.err.println("Error during tree generation in parser");
			System.err.println(e.getStackTrace());
			return null;
		}


		return parser;
	}

}
